package com.example.gateway.filter;

import org.springframework.cloud.gateway.event.FilterArgsEvent;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.List;
import java.util.Map;

/**
 * @author DestinyCc
 * @date 2022/5/7
 */
public class MyRedisRateLimiter extends RedisRateLimiter implements ApplicationContextAware {

    public MyRedisRateLimiter(ReactiveStringRedisTemplate redisTemplate, RedisScript<List<Long>> script, ConfigurationService configurationService) {
        super(redisTemplate, script, configurationService);
    }

    public MyRedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity) {
        super(defaultReplenishRate, defaultBurstCapacity);
    }

    public MyRedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity, int defaultRequestedTokens) {
        super(defaultReplenishRate, defaultBurstCapacity, defaultRequestedTokens);
    }

    @Override
    public void onApplicationEvent(FilterArgsEvent event) {
        Map<String, Object> args = event.getArgs();

        if (args.isEmpty() || !hasRelevantKey(args)) {
            return;
        }

        String routeId = event.getRouteId();
        routeId = routeId + event.getArgs().get("key-resolver").hashCode();
        Config routeConfig = newConfig();
        ConfigurationService configurationService = new ConfigurationService();
        configurationService.setValidator(this.getValidator());
        configurationService.with(routeConfig)
                .name(this.getConfigurationPropertyName()).normalizedProperties(args)
                .bind();
        getConfig().put(routeId, routeConfig);
    }

    private boolean hasRelevantKey(Map<String, Object> args) {
        return args.keySet().stream()
                .anyMatch(key -> key.startsWith(this.getConfigurationPropertyName() + "."));
    }
}
